package name.weily.liba.widgets.filepicker

import name.weily.liba.tools.*
import java.io.File

class FileScanner {
    companion object {
        private const val TAG = "FileScanner";
    }

    enum class State(val value: String) {
        BEGIN("begin"),
        SCAN_FIRST_FOUND("scan_first_found"),
        SAVE_FIRST("save_first"),
        SCANNING("scanning"),
        SCAN_FINISH("scan_finish"),
        SAVE_FINISH("save_finish"),
        END("end");
    }

    private interface Extension {
        fun value() : Array<String>
    }

    enum class Type : Extension {
        AUDIO {
            override fun value(): Array<String> {
                return arrayOf(".mp3", ".mp2", ".wav", ".wma", ".ogg", ".aiff", ".amr",
                    ".ape", ".au", ".flac", ".m4a", ".m4r", ".mmf")
            }
        },
        VIDEO {
            override fun value(): Array<String> {
                return arrayOf(".mp4", ".3gp", ".avi", ".mkv", ".wmv", ".asf", ".mpeg", ".f4v",
                    ".flv", ".mov", ".mpg", ".rmvb", ".rm", ".ts", ".vob")
            }
        },
        IMAGE {
            override fun value(): Array<String> {
                return arrayOf( ".jpg", ".jpeg", ".bmp", ".png", ".gif")
            }
        },
        MEDIA {
            override fun value(): Array<String> {
                return AUDIO.value().plus(VIDEO.value()).plus(IMAGE.value())
            }
        };


    }

    data class FileEntry(val name: String, val ext: String, val absPath: String)

    interface ScanWatcher {
        fun onScanChanged(state: State, list: List<FileEntry>?)
    }

    private val watchers by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { mutableListOf<ScanWatcher>() }
    private val folderCounter by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Counter() }
    private val scanFileCounter by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Counter() }
    private val fileCounter by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { Counter() }


    private fun notifyScanChanged(state: State, list: List<FileEntry>?) {
        watchers.runSync { forEach { it.onScanChanged(state, list) } }
    }

    fun addWatcher(watcher: ScanWatcher) {
        watchers.runSync {
            if (!contains(watcher)) {
                add(watcher)
            }
        }
    }

    fun removeWatcher(watcher: ScanWatcher) {
        watchers.runSync { remove(watcher) }
    }

    fun scan(path: String, type: Array<String>) {
        val file = File(path)
        if (file.exists() && file.isDirectory) {
            fileCounter.reset()
            folderCounter.reset()
            scanFileCounter.reset()
            notifyScanChanged(State.BEGIN, null)
            folderCounter.decrease()
            scanDirectory(file, type)
        } else {
            loge(TAG, "path not found or file is not a directory: $path")
        }
    }

    private fun scanDirectory(file: File, type: Array<String>) {
        AppThreadPool.getInstance().post {
            val files = mutableListOf<FileEntry>()

            file.listFiles()?.forEach {
                if (it.isDirectory) {
                    folderCounter.decrease()
                    scanDirectory(it, type)
                } else {
                    fileCounter.decrease()
                    when {
                        type.contains(".${it.extension}") -> files.add(FileEntry(it.name, it.extension, it.absolutePath))
                        else -> {
//                            logd(TAG, "unknown type=${it.extension}, ignoring ${it.absolutePath}")
                        }
                    }
                }
            }
            if (files.size > 0) {
                if (scanFileCounter.getCount() == 0) {
                    notifyScanChanged(State.SCAN_FIRST_FOUND, files)
                } else {
                    notifyScanChanged(State.SCANNING, files)
                }
                scanFileCounter.decrease(files.size)
            }
            folderCounter.increase()
            if (folderCounter.getCount() == 0) {
                notifyScanChanged(State.SCAN_FINISH, null)
                logd(TAG, "ffffff scan finish found=[${scanFileCounter.getCount()}] file in [${fileCounter.getCount()}] file")
                notifyScanChanged(State.END, null)
            }
        }
    }
}